#pragma once

#include <assert.h>
#include <new>

/** 
 * \ingroup ti6
 *
 * \brief
 * This is a reference counting class and is used to delete a piece
 * of memory when it is no longer used. The referenceCount member of
 * Ref::RefObj indicates the number of users for the object.
 * The class is safe for use with variable length buffers.
 *
 * Copyright (c) Academie voor ICT en Media 2007 - 2008
 */
template<typename T>
class Ref
{
	/** 
	 * \ingroup ti6
	 *
	 * \brief
	 * Store a pointer to the object and the reference count. There
	 * is only one RefObj instance per object: each Ref instance
	 * (indirectly) pointing to this object contains a pointer to
	 * this RefObj instance.
	 *
	 * Copyright (c) Academie voor ICT en Media 2007 - 2008
	 */
	class RefObj
	{
	public:
		size_t referenceCount;
		T *object;
	};

public:
	/**
	 \brief
	 Default constructor, creates an empty reference.
	 */
	Ref()
	{
		_refObj = 0;
	}

	/**
	 \brief
	 Copy constructor. This is used to create a shallow copy of an object.
	 It will increase the reference count.
	 
	 \param &ref The reference to copy.
	 */
	Ref( const Ref &ref )
	{
		_refObj = ref._refObj;
		if( _refObj )
		{
			++_refObj->referenceCount;
		}
	}

	/**
	 \brief
	 Create a counting reference to the specified object. This will take
	 ownership of the object and will delete it when the reference count
	 has dropped to zero (at deletion of the last reference).
	 
	 \param *obj The object for which a reference should be created.
	 */
	Ref( T *obj )
	{
		if( obj )
		{
			_refObj = new RefObj();
			_refObj->referenceCount = 1;
			_refObj->object = obj;
		}
		else
		{
			_refObj = 0;
		}
	}

	/**
	 \brief
	 Deconstructor. This will decrease the reference count and delete the
	 object if this was the last reference.
	 */
	~Ref()
	{
		if( _refObj )
		{
			if( --_refObj->referenceCount == 0 )
			{
				delete _refObj->object;
				delete _refObj;
			}
		}
	}

	/**
	 \brief
	 Make this instance a reference to the object pointed to by ref. This will
	 create a shallow copy, by copying the memory address and increasing the
	 reference count.
	 
	 \param &ref A reference.
	 \return This instance.
	 */
	Ref& operator =( const Ref &ref )
	{
		//Destruct and cop-construct the instance in place. This form of new,
		//which is called 'placement new', does not allocate any memory.
		this->~Ref();
		new (this) Ref( ref );
		return *this;
	}

	/**
	 \brief
	 Dereferencing operator. This can be used to get a normal C++ reference
	 to the object. Use this function only when the reference is assigned to
	 an object.
	 
	 \return A direct C++ reference to the object.
	 */
	T& operator *()
	{
		//When the assertion fails, this is a null reference.
		assert( _refObj );

		return *(_refObj->object);
	}

	/**
	 \brief
	 Dereferencing operator. This can be used to get a normal C++ reference
	 to the object. Use this function only when the reference is assigned to
	 an object.
	 
	 \return A direct C++ reference to the object.
	 */
	const T& operator *() const
	{
		//When the assertion fails, this is a null reference.
		assert( _refObj );

		return *(_refObj->object);
	}

	/**
	 \brief
	 Member access operator. This can be used to directly access the members
	 of the object. Use this function only when the reference is assigned to
	 an object.
	 
	 \return A pointer to the object.
	 */
	T* operator ->()
	{
		//When the assertion fails, this is a null reference.
		assert( _refObj );

		return _refObj->object;
	}

	/**
	 \brief
	 Member access operator. This can be used to directly access the members
	 of the object. Use this function only when the reference is assigned to
	 an object.
	 
	 \return A pointer to the object.
	 */
	const T* operator ->() const
	{
		//When the assertion fails, this is a null reference.
		assert( _refObj );

		return _refObj->object;
	}

	/**
	 \brief
	 Make this an exclusive reference. That is, the reference count should be
	 1 (or 0 for a null reference). If the reference count is larger than 1,
	 a deep copy is created by using the copy constructor of T. Note that this
	 is probably not useful when T is a pointer or when the object is an
	 instance of a subclass of T.
	 */
	void makeSelfContained()
	{
		//Create a version of the referenced object with only one reference. Other references
		//remain unchanged.
		assert( _refObj );

		if( _refObj->referenceCount > 1 )
		{
			RefObj *newRef = new RefObj();
			newRef->object = new T( *_refObj->object );
			newRef->referenceCount = 1;
			--_refObj->referenceCount;
			_refObj = newRef;
		}
	}

private:
	/**
	 An object containing a pointer to the object and the reference count.
	 */
	RefObj *_refObj;
};
